defmodule Stripe.InvoiceTest do
  use Stripe.StripeCase, async: true

  describe "create/1" do
    test "creates an invoice" do
      assert {:ok, %Stripe.Invoice{}} = Stripe.Invoice.create(%{customer: "cus_123"})
      assert_stripe_requested(:post, "/v1/invoices")
    end
  end

  describe "retrieve/1" do
    test "retrieves an invoice" do
      assert {:ok, %Stripe.Invoice{}} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/in_123")
    end
  end

  describe "upcoming/1" do
    test "retrieves an upcoming invoice for a customer" do
      params = %{customer: "cus_123", subscription: "sub_123"}
      assert {:ok, %Stripe.Invoice{}} = Stripe.Invoice.upcoming(params)

      assert_stripe_requested(
        :get,
        "/v1/invoices/upcoming",
        query: %{customer: "cus_123", subscription: "sub_123"}
      )
    end

    test "retrieves an upcoming invoice for a subscription" do
      params = %{subscription: "sub_123"}
      assert {:ok, %Stripe.Invoice{}} = Stripe.Invoice.upcoming(params)
      assert_stripe_requested(:get, "/v1/invoices/upcoming", query: %{subscription: "sub_123"})
    end

    test "retrieves an upcoming invoice for a customer with items" do
      items = [%{plan: "gold", quantity: 2}]
      params = %{customer: "cus_123", subscription_items: items}
      assert {:ok, %Stripe.Invoice{}} = Stripe.Invoice.upcoming(params)

      assert_stripe_requested(
        :get,
        "/v1/invoices/upcoming",
        query: %{
          :customer => "cus_123",
          :"subscription_items[0][plan]" => "gold",
          :"subscription_items[0][quantity]" => 2
        }
      )
    end

    test "can be called with an empty string" do
      params = %{coupon: "", customer: "cus_123"}
      assert {:ok, %Stripe.Invoice{}} = Stripe.Invoice.upcoming(params)

      assert_stripe_requested(
        :get,
        "/v1/invoices/upcoming",
        query: %{customer: "cus_123", coupon: ""}
      )
    end
  end

  describe "update/2" do
    test "updates an invoice" do
      params = %{metadata: %{key: "value"}}
      assert {:ok, %Stripe.Invoice{}} = Stripe.Invoice.update("in_123", params)
      assert_stripe_requested(:post, "/v1/invoices/in_123")
    end
  end

  describe "finalize/2" do
    test "finalizes an invoice" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      assert {:ok, %Stripe.Invoice{} = _paid_invoice} =
               Stripe.Invoice.finalize_invoice(invoice.id, %{})

      assert_stripe_requested(:post, "/v1/invoices/#{invoice.id}/finalize")
    end
  end

  describe "pay/2" do
    test "pays an invoice" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      assert {:ok, %Stripe.Invoice{} = _paid_invoice} = Stripe.Invoice.pay(invoice.id, %{})
      assert_stripe_requested(:post, "/v1/invoices/#{invoice.id}/pay")
    end

    test "pays an invoice with a specific source" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      params = %{source: "src_123"}
      assert {:ok, %Stripe.Invoice{} = _paid_invoice} = Stripe.Invoice.pay(invoice.id, params)

      assert_stripe_requested(:post, "/v1/invoices/#{invoice.id}/pay", body: %{source: "src_123"})
    end
  end

  describe "list/2" do
    test "lists all invoices" do
      assert {:ok, %Stripe.List{data: invoices}} = Stripe.Invoice.list()
      assert_stripe_requested(:get, "/v1/invoices")
      assert is_list(invoices)
      assert %Stripe.Invoice{} = hd(invoices)
    end
  end

  describe "search/2" do
    test "searches invoices" do
      search_query = "name:'fakename' AND metadata['foo']:'bar'"

      assert {:ok, %Stripe.SearchResult{data: invoices}} =
               Stripe.Invoice.search(%{query: search_query})

      assert_stripe_requested(:get, "/v1/invoices/search", query: [query: search_query])
      assert is_list(invoices)
      assert %Stripe.Invoice{} = hd(invoices)
    end
  end

  describe "void/2" do
    test "voids an invoice" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      assert {:ok, %Stripe.Invoice{} = _voided_invoice} = Stripe.Invoice.void_invoice(invoice.id)
      assert_stripe_requested(:post, "/v1/invoices/#{invoice.id}/void")
    end
  end

  describe "send/2" do
    test "sends an invoice" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      assert {:ok, %Stripe.Invoice{} = _sent_invoice} = Stripe.Invoice.send_invoice(invoice.id)
      assert_stripe_requested(:post, "/v1/invoices/#{invoice.id}/send")
    end
  end

  describe "delete/1" do
    test "deletes an invoice" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      assert {:ok, %Stripe.Invoice{} = _sent_invoice} = Stripe.Invoice.delete(invoice.id)
      assert_stripe_requested(:delete, "/v1/invoices/#{invoice.id}")
    end

    test "deletes an invoice does not inclue idempotency key" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      assert {:ok, %Stripe.Invoice{} = _sent_invoice} = Stripe.Invoice.delete(invoice.id)

      refute Map.has_key?(get_stripe_request_headers(), "Idempotency-Key")
    end
  end

  describe "mark_uncollectible/1" do
    test "marks an invoice as uncollectible" do
      {:ok, invoice} = Stripe.Invoice.retrieve("in_123")
      assert_stripe_requested(:get, "/v1/invoices/#{invoice.id}")

      assert {:ok, %Stripe.Invoice{} = _sent_invoice} =
               Stripe.Invoice.mark_uncollectible(invoice.id)

      assert_stripe_requested(:post, "/v1/invoices/#{invoice.id}/mark_uncollectible")
    end
  end
end
